package com.hz.house.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hz.house.bean.entity.CustomerBean;
import com.hz.house.bean.vo.*;
import com.hz.house.common.*;
import com.hz.house.config.WxConfig;
import com.hz.house.dao.CustomerMapper;
import com.hz.house.enums.SystemMsgEnum;
import com.hz.house.token.CurrentUser;
import com.hz.house.token.JWTUtil;
import com.hz.house.token.LoginUser;
import com.hz.house.utils.WxUserInfoDecodeUtil;
import com.hz.house.utils.wxutil.RandomUtils;
import com.hz.house.utils.wxutil.SHA1;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 用户服务
 *
 * @author lfw
 */
@Service
@Slf4j
public class CustomerService {

    @Autowired
    private WxConfig wxConfig;

    @Resource
    private RestTemplate restTemplate;

    @Autowired
    private CustomerMapper userMapper;

    @Autowired
    private JWTUtil jwt;

    @Autowired
    private RedisCacheDataService cacheService;

    @Autowired
    private TCIMService tcIMService;

    @Resource
    private WxMiniService wxMiniService;

    @Autowired
    private RedisTemplate<String, Object> redis;

    private static final String USER_INFO_SESSION_KEY_CACHE = "wx_userinfo_sk:";

    /**
     * 买房好帮手微信公众号ticket
     */
    private static String EVALUATE_WX_MFHBSGZHT_REDIS_KEY = "evaluate_wxgzhaccticket";

    private String GET_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";

    @Autowired
    private SysMsgService sysMsgService;

    @Autowired
    private ElasticsearchTemplate esTemplate;

    @Autowired
    private ThreadPool threadPool;

    /**
     * 默认的用户头像
     */
    private String defaultHeadImg = "https://housessmall-dev-index-img-1258778894."
            + "cos.ap-chengdu.myqcloud.com/sellman/headImg_xiong.png";

    /**
     * <p> 用户登陆接口，有则登陆，没有则注册
     * 处理同一用户并发登录
     *
     * @param jscode
     * @param invitorId
     * @param sceneId
     * @return
     */
    private Map<String, Object> lockers = new ConcurrentHashMap<>();

    public Object userLoin(LoginCondition condition) {
        System.out.println(JSON.toJSONString(condition));
        if (StringUtils.isEmpty(condition.getCode())) {
            return ResultUtil.fail("登陆失败");
        }
        //调用wx接口获取openid和unionid
        WxLoginResultBean wxRes = getWxSession(condition.getCode());
        if (wxRes.getOpenid() == null || wxRes.getSessionKey() == null) {
            return ResultUtil.fail("登陆失败");
        }
        Map<String, Object> m = new HashMap<>();
        Object locker = lockers.get(wxRes.getOpenid());
        if (locker == null) {
            lockers.put(wxRes.getOpenid(), locker = new Object());
        }
        //做一个临时缓存，双重锁
        synchronized (locker) {
            try {
                //除了授权前登陆一次的登陆请求都做临时缓存避免并发调用
                if (condition.getSceneId() == null ||
                        !condition.getSceneId().equals(Constants.USER_LOGIN_SCENEID_WXGETUSERINFO)) {
                    Map<String, Object> mx = this.cacheService.getTempUserLoginRes(wxRes.getOpenid());
                    if (mx != null) {
                        log.info("同一用户并发登录走缓存....");
                        return ResultUtil.ok(mx);//并发登录走缓存
                    }
                }
                //执行登陆操作
                CustomerBean user = userMapper.getUser(wxRes.getOpenid());
                if (user == null) {
                    //新用户注册
                    try {
                        user = registerNewUser(wxRes, condition.getInvitorId(), condition.getSceneId());
                    } catch (Exception e) {
                        e.printStackTrace();
                        log.error("新用户注册异常", e);
                        return ResultUtil.fail("登陆失败");
                    }
                    if (user == null) {
                        //注册失败
                        log.error("新用户注册失败");
                        return ResultUtil.fail("登陆失败");
                    }
                } else {
                    //老用户
                    handleOldUser(user, wxRes, condition.getSceneId());
                    user.setSessionKey(wxRes.getSessionKey());
                }
                //处理是否需要获取用户信息,unionid为空或用户头像没有则需要获取
                if (StringUtils.isEmpty(user.getUnionid()) || StringUtils.isEmpty(user.getHeadImg())) {
                    m.put("needGetUserInfo", true);
                } else {
                    m.put("needGetUserInfo", false);
                }
                //获取token
                LoginUser lu = new LoginUser();
                lu.setUserId(user.getId());
                String token = jwt.createToken(lu);
                m.put("token", token);
                Integer know = user.getIKnow();
                m.put("showTip", !(know == null || know == 0));
                this.cacheService.setTempUserLoginRes(wxRes.getOpenid(), m);
                return ResultUtil.ok(m);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("登陆异常", e);
                return ResultUtil.fail("登陆失败");
            } finally {
                lockers.remove(wxRes.getOpenid());//用完删除,防止内存泄漏
            }
        }
    }

    public ResultBean getJsApiSignature(String url) {
        long timestamp = System.currentTimeMillis() / 1000;
        String randomStr = RandomUtils.getRandomStr();
        String jsapiTicket = getJsApiTicket();
        System.out.println("ticket:" + jsapiTicket);
        String signature = SHA1.genWithAmple("jsapi_ticket=" + jsapiTicket,
                "noncestr=" + randomStr, "timestamp=" + timestamp, "url=" + url);
        System.out.println("signature:" + signature);
        return ResultUtil.ok(WxJsapiSignature
                .builder()
                .timestamp(timestamp)
                .nonceStr(randomStr)
                .url(url)
                .signature(signature)
                .build());
    }

    //注册新用户
    private CustomerBean registerNewUser(WxLoginResultBean wxRes, Integer invitorId, Integer sceneId) throws Exception {
        log.info("新用户注册...");
        int id = cacheService.getIncrementUserId();
        CustomerBean user = new CustomerBean();
        user.setId(id);
        Date now = new Date();
        user.setInviteId(invitorId);
        user.setLevel(1);
        user.setLoginTimes(1);
        user.setOpenid(wxRes.getOpenid());
        user.setRoleType(Constants.USER_TYPE_CUSTOMER);
        user.setSessionKey(wxRes.getSessionKey());
        user.setState(Constants.USER_STATE_NORMAL);
        user.setUnionid(wxRes.getUnionid());
        user.setUpdateDate(now);
        user.setLastActivity(now);
        user.setCreateDate(now);
        user.setSceneId(sceneId);
        //openid长度28
        try {
            user.setName("用户" + user.getOpenid().substring(20));
        } catch (Exception e) {
            user.setName("用户" + user.getOpenid().substring(0, 6));
        }
        user.setHeadImg(defaultHeadImg);
        user.setExp(0);
        int index = this.userMapper.registerUser(user);
        //将用户加入im聊天
        //去线程池执行
        this.threadPool.runInThreadPool(() -> {
            //将信息全的用户加入IM聊天用户列表里
            tcIMService.addCustomerTencentYunIm(user);
        });
        return index == 0 ? null : user;
    }

    //处理是否需要更新sk或unionid
    private void handleOldUser(CustomerBean user, WxLoginResultBean wxRes, Integer sceneId) {

        //根据场景值判断是否需要更新session_key
        if (sceneId != null && Constants.USER_LOGIN_SCENEID_WXGETUSERINFO.equals(sceneId)) {
            redis.opsForValue().set(USER_INFO_SESSION_KEY_CACHE + user.getId(), wxRes.getSessionKey(), 20, TimeUnit.SECONDS);
            log.info("授权登陆场景，缓存sk20秒");
        }
        if (StringUtils.isEmpty(user.getUnionid()) && !StringUtils.isEmpty(wxRes.getUnionid())) {
            user.setUnionid(wxRes.getUnionid());
            log.info("unionid为空，要更新unionid");
            this.userMapper.updateSkOrUnionId(user);
        }
    }


    /**
     * 执行微信登陆
     *
     * @param code
     * @return
     */
    private WxLoginResultBean getWxSession(String code) {
        Map<String, Object> m = new HashMap<>();
        String urlString = "https://api.weixin.qq.com/sns/jscode2session?"
                + "appid={appid}&secret={secret}&js_code={code}&grant_type={grantType}";
        // 向微信接口发起请求
        m.put("appid", wxConfig.getAppid());
        m.put("secret", wxConfig.getSecret());
        m.put("code", code);
        m.put("grantType", "authorization_code");
        String result = restTemplate.getForObject(urlString, String.class, m);
        log.info("登陆wx响应结果：" + result);
        WxLoginResultBean wxRes = new WxLoginResultBean();
        JSONObject jsonObject = JSON.parseObject(result);
        wxRes.setOpenid(jsonObject.getString("openid"));
        wxRes.setSessionKey(jsonObject.getString("session_key"));
        wxRes.setUnionid(jsonObject.getString("unionid"));
        return wxRes;
    }

    /**
     * 获取JsApiTicket
     *
     * @return
     */
    public String getJsApiTicket() {
        String ticket = (String) this.redis.opsForValue().get(EVALUATE_WX_MFHBSGZHT_REDIS_KEY);
        if (StringUtils.isEmpty(ticket)) {
            // 向微信接口发起请求
            Map<String, Object> result = restTemplate.getForObject(wxMiniService.addWxAccessToken(GET_TICKET_URL), Map.class);
            log.info("登陆wx响应结果：" + result);
            if (result.get("errcode").equals(0)) {
                ticket = (String) result.get("ticket");
            }
            this.redis.opsForValue().set(EVALUATE_WX_MFHBSGZHT_REDIS_KEY, ticket, 2, TimeUnit.HOURS);
        }
        return ticket;
    }

    /**
     * 用户授权后保存用户的基本信息
     *
     * @param userInfo
     * @return
     */
    public Object addUserInfo(WxUserInfoBean userInfo, Integer customerId) {

        if (StringUtils.isEmpty(userInfo.getEncryptedData())) {
            return ResultUtil.fail("授权失败");
        }
        String encData = userInfo.getEncryptedData();
        String sk = (String) redis.opsForValue().get(USER_INFO_SESSION_KEY_CACHE + CurrentUser.getCurrentUserId());
        redis.delete(USER_INFO_SESSION_KEY_CACHE + CurrentUser.getCurrentUserId());//删除缓存的sk
        if (StringUtils.isEmpty(sk)) {
            log.info("授权的sk:" + sk);
            return ResultUtil.fail("授权失败");
        }
        //查询sessionkey解密
        String res;
        try {
            res = WxUserInfoDecodeUtil.decryptData(encData, sk, userInfo.getIv());
        } catch (Exception e) {
            e.printStackTrace();
            log.error("授权失败", e);
            return ResultUtil.fail("授权失败");
        }
        JSONObject json = JSON.parseObject(res);
        CustomerBean cus = new CustomerBean();
        cus.setOpenid(json.getString("openId"));
        cus.setUnionid(json.getString("unionId"));
        String headImg = json.getString("avatarUrl");
        //获取高清头像
        headImg = getHeadImgUrl(headImg);
        cus.setHeadImg(headImg == null ? defaultHeadImg : headImg);
        String sex = json.getString("gender");
        cus.setId(CurrentUser.getCurrentUserId());
        if (sex.equals("1")) {
            cus.setGender(Constants.USER_GENDER_MALE);
        } else if (sex.equals("2")) {
            cus.setGender(Constants.USER_GENDER_FEMALE);
        } else {
            cus.setGender(Constants.USER_GENDER_UNKNOWN);
        }
        String nickName = json.getString("nickName");
        if (nickName == null || nickName.equals("undefined") || nickName.equals("null")) {
            cus.setName("用户" + cus.getOpenid().substring(0, 6));
        } else {
            cus.setName(nickName);
        }
        //如果unionid不空，尝试从临时表获取买房帮手公众号的openid
        if (!StringUtils.isEmpty(cus.getUnionid())) {
            String wxgzhOpenid = this.userMapper.getBuyHoseGzhOpenid(cus.getUnionid());
            if (wxgzhOpenid != null) {
                cus.setWxgzhOpenid(wxgzhOpenid);
            }
        }
        List<CustomerBean> customerBeans = userMapper.getUserByOpenId(cus.getOpenid());
        if (!CollectionUtils.isEmpty(customerBeans)) {
            //如果之前存在此用户  不更改之前的头像
            CustomerBean customerBean = customerBeans.get(0);
            cus.setHeadImg(customerBean.getHeadImg());
        }
        //修改用户的基本信息
        int index = this.userMapper.updateUserInfo(cus);
        boolean x = index > 0 && !StringUtils.isEmpty(cus.getUnionid());
        //去线程池执行
        this.threadPool.runInThreadPool(() -> {
            //将信息全的用户加入IM聊天用户列表里
            tcIMService.addCustomerTencentYunIm(cus);
            //如果是置业顾问的话，还要处理其他事情
            doIfUserIsSellMan(cus, customerId);
        });
        return ResultUtil.ok(!x);
    }

    public ResultBean updateHeadImg(String headImg, Integer customerId) {
        CustomerBean cus = userMapper.getCustomerById(customerId);
        cus.setHeadImg(headImg);
        userMapper.updateUserInfo(cus);
        //去线程池执行
        this.threadPool.runInThreadPool(() -> {
            //将信息全的用户加入IM聊天用户列表里
            tcIMService.addCustomerTencentYunIm(cus);
            //如果是置业顾问的话，还要处理其他事情
            doIfUserIsSellMan(cus, customerId);
        });
        return ResultUtil.ok();
    }

    private String getHeadImgUrl(String head) {
        if (StringUtils.isEmpty(head)) {
            return null;
        }
        if (head.equals("undefined") || head.equals("null")) {
            return null;
        }
        //将url最后的132替换为0
        String url = head.substring(0, head.length() - 3) + "0";
        return url;
    }

    private void doIfUserIsSellMan(CustomerBean cus, Integer customerId) {
        SellManCachedBasicInfoBean x = this.cacheService.getSellManBasicInfo(customerId);
        if (x == null) {
            return;
        }
        log.error("授权用户是置业顾问，更新头像到ES");
        //将头像信息更新到es
        BulkRequestBuilder bulk = esTemplate.getClient().prepareBulk();
        UpdateRequest u = new UpdateRequest();
        u.id(customerId + "");
        u.index(Constants.SELL_MAN_ES_INDEX_NAME);
        u.type(Constants.SELL_MAN_ES_TYPE_NAME);
        Map<String, Object> m = new HashMap<>();
        m.put("headImg", cus.getHeadImg());
        u.doc(m);
        bulk.add(u);
        BulkResponse res = bulk.execute().actionGet();
        if (res.hasFailures()) {
            log.error("授权用户是置业顾问，更新头像到ES失败");
        }
        this.cacheService.deleteAllSellManCache();
        this.cacheService.deleteYesterdayStars(null);
        this.cacheService.deleteGodenSellManCache();
    }

    /**
     * 发送楼盘评论回复的系统消息给用户
     *
     * @param msg
     * @return
     */
    public Object sendBuildingCommentReplyMsg(CommentReplyMsgCondition msg) {

        if (msg.getBuildingId() == null) {
            return ResultUtil.fail("楼盘id为空");
        }
        if (StringUtils.isEmpty(msg.getContent())) {
            return ResultUtil.fail("消息内容为空");
        }
        if (StringUtils.isEmpty(msg.getTitle())) {
            return ResultUtil.fail("消息标题为空");
        }
        if (msg.getToCustomerId() == null) {
            return ResultUtil.fail("发送给谁的ID不能为空");
        }
        SystemMsgEnum type = SystemMsgEnum.COMMENT_REPLY;
        String toPageParam = "id=" + msg.getBuildingId();
        sysMsgService.sendSysMsg(msg.getToCustomerId(), msg.getContent(),
                msg.getTitle(), toPageParam, type);
        return ResultUtil.ok();
    }

    /**
     * 发送楼盘评论点赞的消息给系统用户
     *
     * @param msg
     * @return
     */
    public Object sendBuildingCommentPraiseMsg(CommentReplyMsgCondition msg) {
        if (msg.getBuildingId() == null) {
            return ResultUtil.fail("楼盘id为空");
        }
        if (StringUtils.isEmpty(msg.getContent())) {
            return ResultUtil.fail("消息内容为空");
        }
        if (StringUtils.isEmpty(msg.getTitle())) {
            return ResultUtil.fail("消息标题为空");
        }
        if (msg.getToCustomerId() == null) {
            return ResultUtil.fail("发送给谁的ID不能为空");
        }
        SystemMsgEnum type = SystemMsgEnum.COMMENT_PRAISE;
        String toPageParam = "id=" + msg.getBuildingId();
        sysMsgService.sendSysMsg(msg.getToCustomerId(), msg.getContent(),
                msg.getTitle(), toPageParam, type);
        return ResultUtil.ok();
    }

    public Object sendFaqAnsweredMsg(FaqAnswerMsgInfo msg) {
        if (StringUtils.isEmpty(msg.getContent())) {
            return ResultUtil.fail("消息内容为空");
        }
        if (StringUtils.isEmpty(msg.getTitle())) {
            return ResultUtil.fail("标题为空");
        }
        if (msg.getFaqId() == null) {
            return ResultUtil.fail("问答id为空");
        }
        if (msg.getToCustomerId() == null) {
            return ResultUtil.fail("目标用户的id为空");
        }
        String param = "faqId=" + msg.getFaqId();
        SystemMsgEnum type = SystemMsgEnum.FAQ_NEW_ANSWER;
        sysMsgService.sendSysMsg(msg.getToCustomerId(), msg.getContent(), msg.getTitle(), param, type);
        return ResultUtil.ok();
    }

    /**
     * 发送置业顾问被关注的消息给置业顾问
     *
     * @return
     */
    public Object focusSellManMsg(Integer sellManCustomerid, Integer focusCustomerId) {
        if (sellManCustomerid == null) {
            return ResultUtil.fail("置业顾问id为空");
        }
        if (focusCustomerId == null) {
            return ResultUtil.fail("关注者的id为空");
        }
        String title = "有新用户关注您";
        CustomerBean customer = this.userMapper.getCustomerById(focusCustomerId);
        String content = "恭喜,用户" + customer.getName() + "关注了您!";
        sysMsgService.sendSysMsg(sellManCustomerid, content, title, null, SystemMsgEnum.FOCUS);
        return ResultUtil.ok();
    }

    /**
     * 分享加分消息推送
     *
     * @return
     */
    public void sendVisitMsgToSellMan(Integer sellManCustomerid, Integer visitCustomerId) {
        //加分类型
        if (sellManCustomerid == null || visitCustomerId == null) {
            return;
        }
        //访问者信息
        CustomerBean customer = this.userMapper.getCustomerById(visitCustomerId);
        //站内推送
        String title = "访问提示";
        String content = "用户" + customer.getName() + "通过分享访问了您的主页!";
        sysMsgService.sendSysMsg(sellManCustomerid, content, title, null, SystemMsgEnum.VISIT_MYHOMEPAGE_BYSHATE);

    }


    public Object closeHomePageTip(int customerId) {
        this.userMapper.closeIknow(customerId);
        return ResultUtil.ok();
    }


}
